﻿using Elsa.Models;
using Elsa.Persistence;
using Elsa.Persistence.Specifications;
using Elsa.Persistence.Specifications.WorkflowDefinitions;
using Elsa.Serialization;
using Humanizer;
using Microsoft.AspNetCore.Http;
using System.IO.Compression;
using System.Text;

namespace Zhp.Workflow.Middleware
{
    public class WorkflowMiddleware
    {
        private readonly RequestDelegate _next;

        public WorkflowMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            if (WorkflowElsaApi.apis.TryGetValue(context.Request.Path, out string method))
            {
                var methodInfo = typeof(WorkflowElsaApi).GetMethod(method);
                var task = (Task)methodInfo.Invoke(new WorkflowElsaApi(), new object[] { context });
                await task;
                return;
            }
            
            await _next(context);

            if (context.Response.StatusCode == 404)
            {
                await context.Response.WriteAsync(string.Empty);
            }
        }
    }

    public class WorkflowElsaApi
    {
        public static Dictionary<string, string> apis = new Dictionary<string, string>()
        {
            { "/v1/workflow-definitions/backup", "Backup" }
        };

        /// <summary>
        /// 特殊处理备份流程
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Backup(HttpContext context)
        {
            context.Response.ContentType = "application/zip";
            context.Response.Headers.Append($"Content-Disposition", $"attachment; filename=\"Workflows-{DateTime.Now:yyyy-MM-dd-HH-mm}.zip\"");

            var provider = context.RequestServices;
            var workflowDefinitionStore = (IWorkflowDefinitionStore)provider.GetService(typeof(IWorkflowDefinitionStore));
            var contentSerializer = (IContentSerializer)provider.GetService(typeof(IContentSerializer));
            VersionOptions? version = VersionOptions.Latest;
            var specification = new VersionOptionsSpecification(version.Value).And(new TenantSpecification<WorkflowDefinition>(null));

            var items = await workflowDefinitionStore.FindManyAsync(specification);
            using var zipArchive = new ZipArchive(context.Response.BodyWriter.AsStream(), ZipArchiveMode.Create);
            {
                foreach (var workflowDefinition in items)
                {
                    var json = contentSerializer.Serialize(workflowDefinition);
                    var bytes = Encoding.UTF8.GetBytes(json);
                    var hasWorkflowName = !string.IsNullOrWhiteSpace(workflowDefinition.Name);
                    var workflowName = hasWorkflowName ? workflowDefinition.Name!.Trim() : workflowDefinition.DefinitionId;

                    var fileName = hasWorkflowName
                        ? $"{workflowName.Underscore().Dasherize().ToLowerInvariant()}.json"
                        : $"workflow-definition-{workflowName.Underscore().Dasherize().ToLowerInvariant()}.json";

                    var zipEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
                    await using var zFile = zipEntry.Open();
                    await zFile.WriteAsync(bytes);
                }
            }
        }
    }
}
